import ConfigParser
import httplib, urllib
from xml.dom.minidom import parseString
import dateutil.parser
import dateutil.tz as tz
import datetime
import xml.etree.ElementTree as XMLParser
from syncfolder import SyncFolder 
from syncfile import SyncFile
DEBUG = True

class SugarSync:
    def __init__(self, conf=None, username=None, 
                 password=None, apikey=None, privatekey=None):
        if conf != None:
            self._ConfSetup(conf)
        else:
            self._ParamSetup(username, password, apikey, privatekey)

    def _ConfSetup(self, conf):
        """ Config file based credential setup

            input:
                conf - ConfigParser.RawConfigParser() open to the configuration file (similar to the
                following)
                        conf = ConfigParser.RawConfigParser()
                        conf.read("conf.ini")
                        api = SugarSync(conf)
        """
        self._cred = {}
        try:
            self._cred["username"] = conf.get("SugarSync", "username")
            self._cred["password"] = conf.get("SugarSync", "password")
            self._cred["apikey"] = conf.get("SugarSync", "apikey")
            self._cred["privatekey"] = conf.get("SugarSync", "privatekey")
        except:
            print "Error: Could not read sugar sync configuration"
            return
        self._Authenticate()

    def _ParamSetup(self, username, password, apikey, privatekey):
        """ parameterised credential setup """
        self._cred = {}
        self._cred["username"] = username
        self._cred["password"] = password
        self._cred["apikey"] = apikey
        self._cred["privatekey"] = privatekey
        self._Authenticate()

    def _Authenticate(self):
        """ Authenticate with SugarSync, This should not be called externally
            
            This authentication stores the auth url and its experation in 
            self._auth = {"url": URL VALUE, "exp" : Datetime experation }
        """
        # Connect to sugar sync api and authenticate
        conn = httplib.HTTPSConnection("api.sugarsync.com")

        authstring = '''<?xml version="1.0" encoding="UTF-8" ?>
<authRequest>
 <username>%(username)s</username>
 <password>%(password)s</password>
 <accessKeyId>%(apikey)s</accessKeyId>
 <privateAccessKey>%(privatekey)s</privateAccessKey>
</authRequest>
''' % self._cred 

        params = authstring
        print params
        headers = {"Content-Type" : "application/xml; charset=UTF-8", "Content-Length" : str(len(params)),
                   "Host" : "api.sugarsync.com" }
        conn.request("POST", "/authorization", params, headers)
        response = conn.getresponse()
        status = response.status
        if int(status) != 201:
            print "Error Authenticating with credentials"
            print response.read()
            conn.close()
            return 

        ##
        # This is the URL we need to send requests too
        auth_key = response.getheader("location")

        content = response.read()
        conn.close()
        dom = parseString(content)
        expiration = dom.getElementsByTagName("expiration")[0].toxml()
        expiration = expiration.replace('<expiration>','').replace('</expiration>','')
        userurl = dom.getElementsByTagName("user")[0].toxml()
        userurl = userurl.replace('<user>','').replace('</user>','')
        userurl = userurl.split("/")
        pos = userurl.index("user")
        userurl = "/" + "/".join(userurl[pos:])

        date = dateutil.parser.parse(expiration)
        local_zone = tz.tzlocal()
        date = date.astimezone(local_zone)
        self._auth = {"url" : auth_key, "exp" : date, "user" : userurl}

        if DEBUG == True:
            print "Authentication Information\n\tAuthURL: %(url)s\n\tExpiration: %(exp)s\n\tUserUrl = %(user)s" % self._auth 

    def _CheckAuthorization(self):
        """ Checks to make sure that the authentication is valid and not expired """
        local_zone =  tz.tzlocal()
        date = datetime.datetime.now().replace(tzinfo=local_zone)

        ## Check to see if the expiration time is less then the current time
        #  if so reauthenticate
        if (self._auth["exp"] - date).days < 0:
            self._Authenticate()

    def _SendRequest(self, url, postdata=None, putdata=None, delete=False):
        """ Send httplib request to sugarsync. Returns {"status", "headers", "data"} """
        self._CheckAuthorization()
        ret = {}

        headers = {"Content-Type" : "application/xml; charset=UTF-8",
                   "Host" : "api.sugarsync.com", "Authorization" : self._auth["url"] }
    
        if postdata != None:
            headers["Content-Length"] = str(len(postdata))
        elif putdata != None:
            headers["Content-Length"] = str(len(putdata))

        conn = httplib.HTTPSConnection("api.sugarsync.com")

        if postdata != None:
            conn.request("POST", url, postdata, headers)
        elif putdata != None:
            conn.request("PUT", url, putdata, headers)
        elif delete == True:
            conn.request("DELETE", url, headers=headers)
        else:
            conn.request("GET", url, headers=headers)

        response = conn.getresponse()
        ret["status"] = response.status
        ret["headers"] = response.getheaders()
        ret["data"] = response.read()
        ret["url"] = url
        conn.close()
        if DEBUG == True:
            print "PostSendRequest\n\tURL:%(url)s\n\tResponse Code:%(status)s" % ret
            for x in ret["headers"]:
                print "\tHeader: " + str(x)
            if len(ret["data"]) < 40000:
                print "\tData:" + ret["data"]

        return ret

    def _GetFolders(self, url):
        """ internal function to get folders """
        ret = []
        reqret = self._SendRequest(url)
        et = XMLParser.XML(reqret["data"])
        folders = et
        for f in folders:
            name = f.find("displayName").text
            ref = f.find("ref").text
            contents = f.find("contents").text

            ref = ref.split("/")
            ref = "/" + "/".join(ref[ref.index("folder"):] )
            
            contents = contents.split("/")
            contents = "/" + "/".join(contents[contents.index("folder"):])

            if DEBUG == True:
                print "Folder " + name
                print "\tRefUrl: " + ref
                print "\tContentUrl: " + contents

            ret.append(SyncFolder(name, ref, contents))
        return ret


    def _GetFiles(self, url):
        """ internal function to get files in a folder """
        ret = []
        reqret = self._SendRequest(url)
        et = XMLParser.XML(reqret["data"])
        folders = et
        for f in folders:
            name = f.find("displayName").text
            ref = f.find("ref").text
            size = f.find("size").text

            lastModified = f.find("lastModified").text
            lastModified = dateutil.parser.parse(lastModified)
            local_zone = tz.tzlocal()
            lastModified = lastModified.astimezone(local_zone)
            if f.find("fileData") != None:
                fileData = f.find("fileData").text
            else:
                fileData = None

            ref = ref.split("/")
            ref = "/" + "/".join(ref[ref.index("file"):] )

            if fileData != None:
                fileData = fileData.split("/")
                fileData = "/" + "/".join(fileData[fileData.index("file"):] )
            else:
                fileData = ref + "/data"

            if DEBUG == True:
                print "File: " + name
                print "\tRefUrl: " + ref
                print "\tsize: " + size
                print "\tLastModified: " + str(lastModified)
                print "\tFileDataURL: " + str(fileData)

            ret.append(SyncFile(name, ref, size, lastModified, fileData))
        return ret

    def GetFolders(self, folder):
        """ Get Folders of a SyncFolder class. Returns a list of SyncFolders 
        
            https://www.sugarsync.com/dev/api/method/get-folders.html
        """
        ret = []
        requrl = folder.GetContentUrl() + "?type=folder"
        return self._GetFolders(requrl)

    def ListSyncFolders(self):
        """ List all folders availible for syncing via sugar sync. Returns a list of SyncFolder classes.

            https://www.sugarsync.com/dev/api/method/get-syncfolders.html
        """
        ret = []
        requrl = self._auth["user"] + "/folders/contents"
        return self._GetFolders(requrl)

    def ListFiles(self, folder):
        """ List all the files in the folder specified. Returns a list of SyncFile classes.

            https://www.sugarsync.com/dev/api/method/get-folder-contents.html
        """
        ret = []
        requrl = folder.GetContentUrl() + "?type=file"
        return self._GetFiles(requrl)

    def GetFile(self, file):
        """ Download file data, returns a string containing the file data

            https://www.sugarsync.com/dev/api/method/get-file-data.html
        """
        ret = self._SendRequest(file.GetDataUrl())
        length = 0
        for x in ret["headers"]:
            if x[0] == "content-length":
                length = int(x[1])

        return (length,ret["data"])

    def WriteFile(self, file, data):
        """ Write data to a file, returns true or false depending on operation completion
            
            https://www.sugarsync.com/dev/api/method/put-file-data.html
        """
        url = file.GetDataUrl()
        reqret = self._SendRequest(url, putdata=data)
        if int(reqret["status"]) >= 200 and int(reqret["status"]) < 300:
            return True
        return False

    def CreateFolder(self, parentfolder, foldername):
        """ Create a folder. Takes a parentfolder (SyncFolder Class) and a Folder Name (string). 
            Returns true if folder created.

            https://www.sugarsync.com/dev/api/method/create-folder.html
        """
        reqdata = '''<?xml version="1.0" encoding="UTF-8" ?>
<folder>
   <displayName>%s</displayName>
</folder>''' 
        reqdata = reqdata % foldername
        ret = self._SendRequest(parentfolder.GetRefUrl(), postdata=reqdata)
        if int(ret["status"]) >= 200 and int(ret["status"]) < 300:
            return True
        return False
        
    def CreateFile(self, folder, filename):
        """ Create a file in a folder. Takes a folder (SyncFolder Class) and a file name (string). 
            Returns true if file created.

            https://www.sugarsync.com/dev/api/method/create-file.html
        """
        reqdata = '''<?xml version="1.0" encoding="UTF-8" ?>
<file>
  <displayName>%s</displayName>
</file>'''

        reqdata = reqdata % filename
        ret = self._SendRequest(folder.GetRefUrl(), postdata=reqdata)
        if int(ret["status"]) >= 200 and int(ret["status"]) < 300:
            return True
        return False

    def DeleteFile (self, file):
        """ Deletes a file. Input is a SyncFile class for the file to be deleted.

            https://www.sugarsync.com/dev/api/method/delete-file.html
        """
        url = file.GetRef()
        ret = self._SendRequest(url, delete=True)
        if int(ret["status"]) >= 200 and int(ret["status"]) < 300:
            return True
        return False

    def DeleteFolder (self, folder):
        """ Deletes a folder. Input is a SyncFolder class for the folder to be deleted. Returns true on deletion.

            https://www.sugarsync.com/dev/api/method/delete-folder.html
        """
        url = folder.GetRefUrl()
        ret = self._SendRequest(url, delete=True)
        if int(ret["status"]) >= 200 and int(ret["status"]) < 300:
            return True
        return False


# For testing
if __name__ == '__main__':
    conf = ConfigParser.RawConfigParser()
    conf.read("conf.ini")
    q = SugarSync(conf=conf)
    ret = q.ListSyncFolders()
    test_folder = None
    for x in ret:
        if "Magic" in x.GetName():
            test_folder = x
            break

    ret = q.GetFolders(test_folder)
    for x in ret:
        if "Test" in x.GetName():
            test_folder = x
            break

    ## List the files 
    folderList = q.ListFiles(test_folder)

    ## locate the file name
    filename = None
    for x in folderList:
        if x.GetName() == "plugin.video.gomtv.net-0.0.6.zip":
            filename = x
            break

    ## Get the file datea
    data = q.GetFile(filename)

    f = open(r"C:\Users\Owner\test_sugarsync.zip","wb")
    f.write(data[1])
    f.close()

    status = q.WriteFile(filename, data[1])
    print status
    status = q.CreateFolder(test_folder, "theone")
    print status
    status = q.CreateFile(test_folder, "tfile.txt")
    print status

    folderList = q.ListFiles(test_folder)
    for x in folderList:
        if "tfile.txt" in x.GetName():
            status = q.DeleteFile(x)
            print status

    folderList = q.GetFolders(test_folder)
    for x in folderList:
        if "theone" in x.GetName():
            status = q.DeleteFolder(x)
            print status